#include <thread>
#include <chrono>
//#include <glibmm/utility.h>
#include <glib.h>
#include <string>
#include <giomm/socket.h>
#include <giomm/inetsocketaddress.h>
#include <giomm/inetaddress.h>
#include <giomm/socketsource.h>

#include "ServiceConnection.h"
#include "AdbHelper.h"
#include "MessageHelper.h"
#include "App.h"

#include "json.hpp"
#include "WatchDogSource.h"

namespace Gnodroid
{

    ServiceConnection::ServiceConnection() :
        Service(SERVICE_CONNECTION)
    {}

    bool 
    ServiceConnection::startServerAsyn(sigc::slot<bool, bool> slotCb)
    {
        //run in other thread
        auto threadRunner =
            [slotCb](ServiceConnection* self)->void {
                //using namespace std::chrono_literals;
                //std::this_thread::sleep_for(5s);
                bool result = self->startServer();
                //run in main thread
                auto slotRunner = [result, slotCb]()->bool {
                  slotCb(result);
                  return false;
                };
                Glib::RefPtr<Glib::MainContext> mainContext =
                    App::getMainContext();
                mainContext->invoke(sigc::slot<bool>(slotRunner));
        };
        std::thread worker(threadRunner, this);
        worker.detach();
        return true;
    }

    bool
    ServiceConnection::checkAdb(std::string path)
    {
        if (path == "") {
            path = m_strAdbPath;
        }
        return AdbHelper::checkAdb(path);
    }

    std::string
    ServiceConnection::getAdbPath()
    {
        return m_strAdbPath;
    }

    std::string
    ServiceConnection::getDeviceId()
    {
        return m_strDeviceId;
    }

    bool
    ServiceConnection::setAdbPath(std::string path)
    {
        m_strAdbPath = path;
        return true;
    }

    int
    ServiceConnection::getAnPort()
    {
        return m_iAnPort;
    }

    int
    ServiceConnection::getPcPort()
    {
        return m_iPcPort;
    }

    std::shared_ptr<std::list<DeviceInfo> >
    ServiceConnection::getDeviceList()
    {
        return AdbHelper::getDeviceList(m_strAdbPath);
    }

    bool
    ServiceConnection::startServer()
    {
        return AdbHelper::startServer(m_strAdbPath);
    }

    void
    ServiceConnection::start()
    {}

    void ServiceConnection::stop()
    {}

/*
    void
    ServiceConnection::emitSignalConnected()
    {}

    void
    ServiceConnection::emitSignalConnectFailed()
    {}

    void
    ServiceConnection::emitSignalDisconnected()
    {}

*/
    bool
    ServiceConnection::connect(std::string id)
    {
        if (m_bIsConnecting) {
            return false;
        }
        m_bIsConnecting = true;
        if(!AdbHelper::mapPort(m_strAdbPath,
                               id, m_iPcPort, m_iAnPort)) {
            m_bIsConnecting = false;
            emitSignalConnectFailed();
            return false;
        }

        std::thread connectThread(sigc::mem_fun(*this, &ServiceConnection::__connectThread), id);
        connectThread.detach();
        return true;

    }

    bool
    ServiceConnection::disconnect()
    {
        if (!m_bIsConnecting) {
            return false;
        }
        //直接打破连接的事件循环
        m_rpMainLoop->quit();
        return true;
    }


    //todo: 要求线程安全
    bool
    ServiceConnection::isConnecting()
    {
        return m_bIsConnecting;
    }


    void
    ServiceConnection::emitSignalConnected()
    {
        //ServiceConnection* self = this;
        auto mainContext = App::getCurrentApp()
            ->getMainContext();
        mainContext->invoke([this]()->bool{
                                this->signal_connected().emit(this->getDeviceId());
                                return false;
                            });
    }

    void
    ServiceConnection::emitSignalConnectFailed()
    {
        //ServiceConnection* self = this;
        auto mainContext = App::getCurrentApp()
            ->getMainContext();
        mainContext->invoke([this]()->bool{
                                this->signal_connectfailed().emit(this->getDeviceId());
                                return false;
                            });
    }

    void
    ServiceConnection::emitSignalDisconnected()
    {
        //ServiceConnection* self = this;
        auto mainContext = App::getCurrentApp()
            ->getMainContext();
        mainContext->invoke([this]()->bool{
                                this->signal_disconnected().emit(this->getDeviceId());
                                return false;
                            });
    }


    sigc::signal<void, std::string>
    ServiceConnection::signal_connected()
    {
        return m_signalConnected;
    }

    sigc::signal<void, std::string>
    ServiceConnection::signal_connectfailed()
    {
        return m_signalConnectFailed;
    }

    sigc::signal<void, std::string>
    ServiceConnection::signal_disconnected()
    {
        return m_signalDisconnected;
    }

    void
    ServiceConnection::__connectThread(std::string id)
    {
        LOG("run in connection");

        m_strDeviceId = id;
        //using namespace nlohmann;

        //json j;
        char* jsonStr = R"({"service":"Connection","method":"StartConnection"})";
        //char* buffData = new char[std::strlen(jsonStr) + 4];
        gint32 size = std::strlen(jsonStr) + 1;
        //htobe32(size);
        /*
        gint32 sizeBE = g_htonl(size);
        std::memcpy(buffData, (char*)&sizeBE, 4);
        std::memcpy(buffData + 4, jsonStr, size);
        size += 4;
        */

        auto socket = Gio::Socket::create(Gio::SOCKET_FAMILY_IPV4,
                                          Gio::SOCKET_TYPE_STREAM,
                                          Gio::SOCKET_PROTOCOL_DEFAULT);
        try {
            socket->connect(Gio::InetSocketAddress::create(Gio::InetAddress::create("127.0.0.1"), m_iPcPort));
        } catch (...) {
            LOG_ERROR("connect failed");
            m_bIsConnecting = false;
            emitSignalConnectFailed();
            return;
            //goto exit_failed;
        }

        socket->set_timeout(2);
/*
        try {
            gssize sizeSend = 0;
            sizeSend += socket->send(buffData, size);
            while (sizeSend != size) {
                sizeSend += socket->send(buffData + sizeSend, size - sizeSend);
            }
        } catch (...) {
            LOG_ERROR("send faild");
            return;
        }
*/
        //发送连接建立请求
        if (!MessageHelper::sendMessage(jsonStr, size, socket)) {
            LOG_ERROR("send messager failed");
            m_bIsConnecting = false;
            emitSignalConnectFailed();
            return;
        }

        m_rpMainContext = Glib::MainContext::create();
        m_rpMainLoop = Glib::MainLoop::create(m_rpMainContext);

        bool isFirstMessage = true;         //是否收到过至少一次消息

        auto watchDog = WatchDogSource::create([this]()->bool{
                                                   LOG("dog hungry");
                                                   m_rpMainLoop->quit();
                                                   return false;
                                               }, 2000);

        auto socketSource = socket->create_source(Glib::IO_IN);
        socketSource->connect([&socket, &watchDog, &isFirstMessage, this](Glib::IOCondition ioc)->bool{

            char* buffRecData = nullptr;
            gssize size = 0;
            if (!MessageHelper::receiveMessage(buffRecData, size, socket)) {
                LOG_ERROR("receive messager failed");
                return false;
            }

            //todo: 根据JSON检查喂狗消息的正确性
            //char* buffLog = new char[size + 1];
            //buffLog[size] = '\0';
            //std::memcpy(buffLog, buffRecData, size);

            //LOG(buffRecData);
            //delete [] buffLog;
            watchDog->feedDog();
            delete [] buffRecData;

            //只在第一次成功接收到信息的时候发送连接成功信号
            if (isFirstMessage) {
                //emitSignalDisconnected();
                emitSignalConnected();
                isFirstMessage = false;
            }

            return true;
        });

        //启动事件循环
        watchDog->attach(m_rpMainContext);
        socketSource->attach(m_rpMainContext);
        m_rpMainLoop->run();
/*
        gint32 dataSize = 0;
        char buffDataSize[4];

        try {
            while (true) {
                socket->receive(buffDataSize, 4);
                dataSize = *reinterpret_cast<gint32*>(buffDataSize);
                dataSize = g_ntohl(dataSize);
                char* buffRecData = new char[dataSize + 1];
                buffRecData[dataSize] = '\0';
                gssize readSize = 0;
                readSize += socket->receive(buffRecData, dataSize);
                while (readSize != dataSize) {
                    readSize += socket->receive(buffRecData + readSize, dataSize - readSize);
                }
                LOG(buffRecData);
                delete [] buffRecData;
            }
        } catch (...) {
            LOG_ERROR("recive failed");
            return;
        }
*/
//exit_failed:
        socket->close();
        //将当前状态置为未连接
        m_bIsConnecting = false;
        m_rpMainContext.reset();
        m_rpMainLoop.reset();

        //如果看门狗未经至少一次喂食，则认为没有建立过连接，连接失败
        if (isFirstMessage) {
            emitSignalConnectFailed();
        } else {
            emitSignalDisconnected();
        }
        //emitSignalDisconnected();
        LOG("exit connection");
        return;
    }


}
